package vip.xiaonuo.common.util;

import cn.hutool.core.io.FileUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.excel.write.style.row.AbstractRowHeightStyleStrategy;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.springframework.web.multipart.MultipartFile;
import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.common.listener.CommonExcelListener;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author zjt
 * @description
 * @date 2024/12/5 18:38
 */
@Slf4j
public class CommonExcelUtil {

    private static final String EXCEL_ERROR = "Excel转换失败";

    /**
     * 同步导入(适用于小数据量)
     *
     * @param multipartFile 文件
     * @return 转换后集合
     */
    public static <T> List<T> importExcel(MultipartFile multipartFile, Class<T> clazz) {
        try {
            return EasyExcel.read(multipartFile.getInputStream()).head(clazz).autoCloseStream(false).sheet().doReadSync();
        } catch (Exception e) {
            log.error(EXCEL_ERROR, e);
            throw new CommonException(EXCEL_ERROR);
        }
    }

    public static <T> List<T> importExcel(MultipartFile multipartFile, Class<T> clazz, CommonExcelListener<T> commonExcelListener) {
        try {
            EasyExcel.read(multipartFile.getInputStream(), clazz, commonExcelListener).sheet().doRead();
            return commonExcelListener.getExcelResult();
        } catch (Exception e) {
            log.error(EXCEL_ERROR, e);
            throw new CommonException(EXCEL_ERROR);
        }
    }

    public static <T> List<T> importExcel(File file, Class<T> clazz) {
        try {
            return EasyExcel.read(file).head(clazz).sheet().doReadSync();
        } catch (Exception e) {
            log.error(EXCEL_ERROR, e);
            throw new CommonException(EXCEL_ERROR);
        }
    }

    public static <T> List<T> importExcel(File file, Class<T> clazz, int startRow, int readRows) {
        try (InputStream inputStream = new FileInputStream(file)) {
            List<T> fullList = new ArrayList<>();
            EasyExcel.read(inputStream)
                    .head(clazz)
                    .sheet()
                    .headRowNumber(1)// 表头行号（1）
                    .registerReadListener(new AnalysisEventListener<T>() {
                        private int rowCount = 0;

                        @Override
                        public void invoke(T data, AnalysisContext context) {
                            if (rowCount >= startRow && rowCount < startRow + readRows) {
                                fullList.add(data);
                            }
                            rowCount++;
                        }

                        @Override
                        public void doAfterAllAnalysed(AnalysisContext context) {
                            log.debug("Excel解析完成，共读取{}行", rowCount);
                        }
                    })
                    .doRead();
            return fullList;
        } catch (Exception e) {
            log.error("Excel导入失败", e);
            throw new CommonException("Excel解析错误: " + e.getMessage());
        }
    }

    public static <T> List<T> importExcel(String filePath, Class<T> clazz) {
        try (FileInputStream inputStream = new FileInputStream(filePath)) {
            return EasyExcel.read(inputStream)
                    .head(clazz)
                    .autoCloseStream(true)  // 设置为true让EasyExcel自动关闭流
                    .sheet()
                    .doReadSync();
        } catch (IOException e) {
            log.error("读取本地Excel文件失败，文件路径: {}, 错误信息:", filePath, e);
            throw new CommonException("读取本地Excel文件失败");
        }
    }

    /**
     * 获取Excel总行数
     *
     * @param file          文件
     * @param excludeHeader 是否排除表头
     * @return
     * @throws IOException
     */
    public static int getExcelRowCount(MultipartFile file, boolean excludeHeader) throws IOException {
        AtomicInteger rowCount = new AtomicInteger(0);
        EasyExcel.read(file.getInputStream(), new AnalysisEventListener<>() {
            @Override
            public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
                if (!excludeHeader) {
                    rowCount.incrementAndGet(); // 包含表头计数
                }
            }

            @Override
            public void invoke(Object data, AnalysisContext context) {
                rowCount.incrementAndGet();
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
            }
        }).sheet().doRead();
        return rowCount.get();
    }

    public static <T> void exportExcel(String title, List<T> result, Class<T> clazz, HttpServletResponse response) {
        File tempFile = null;
        try {
            String fileName = title + CommonDateUtil.getMillisecondTime() + ".xlsx";
            // 创建临时文件
            tempFile = FileUtil.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
            // 头的策略
            WriteCellStyle headWriteCellStyle = new WriteCellStyle();
            WriteFont headWriteFont = new WriteFont();
            headWriteFont.setFontHeightInPoints((short) 14);
            headWriteCellStyle.setWriteFont(headWriteFont);
            // 水平垂直居中
            headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
            headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            // 内容的策略
            WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
            // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
            contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
            // 内容背景白色
            contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
            WriteFont contentWriteFont = new WriteFont();
            // 内容字体大小
            contentWriteFont.setFontHeightInPoints((short) 12);
            contentWriteCellStyle.setWriteFont(contentWriteFont);
            //设置边框样式，细实线
            contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
            contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
            contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
            contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
            // 水平垂直居中
            contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
            contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
            HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle,
                    contentWriteCellStyle);
            // 写excel
            EasyExcel.write(tempFile.getPath(), clazz)
                    // 自定义样式
                    .registerWriteHandler(horizontalCellStyleStrategy)
                    // 自动列宽
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    // 设置第一行字体
                    .registerWriteHandler(new CellWriteHandler() {
                        @Override
                        public void afterCellDispose(CellWriteHandlerContext context) {
                            WriteCellData<?> cellData = context.getFirstCellData();
                            WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();
                            Integer rowIndex = context.getRowIndex();
                            if (rowIndex == 0) {
                                WriteFont headWriteFont = new WriteFont();
                                headWriteFont.setFontName("宋体");
                                headWriteFont.setBold(true);
                                headWriteFont.setFontHeightInPoints((short) 16);
                                writeCellStyle.setWriteFont(headWriteFont);
                            }
                        }
                    })
                    // 设置表头行高
                    .registerWriteHandler(new AbstractRowHeightStyleStrategy() {
                        @Override
                        protected void setHeadColumnHeight(Row row, int relativeRowIndex) {
                            if (relativeRowIndex == 0) {
                                // 表头第一行
                                row.setHeightInPoints(34);
                            } else {
                                // 表头其他行
                                row.setHeightInPoints(30);
                            }
                        }

                        @Override
                        protected void setContentColumnHeight(Row row, int relativeRowIndex) {
                            // 内容行
                            row.setHeightInPoints(20);
                        }
                    })
                    .sheet(CommonDateUtil.getTodayStrB())
                    .doWrite(result);
            CommonDownloadUtil.download(tempFile, response);
        } catch (Exception e) {
            log.error(">>>Excel导出异常：", e);
            try {
                CommonResponseUtil.renderError(response, "导出失败");
            } catch (IOException ex) {
                throw new CommonException("导出失败");
            }
        } finally {
            FileUtil.del(tempFile);
        }
    }

    /**
     * 使用EasyExcel导出数据到本地Excel文件
     *
     * @param filePath 导出的文件路径（包含文件名）
     * @param dataList 要导出的数据列表
     * @param clazz    数据对象的Class
     * @param <T>      泛型类型
     */
    public static <T> void exportExcel(String filePath, List<T> dataList, Class<T> clazz) {
        // 使用自适应列宽策略
        EasyExcel.write(filePath, clazz)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .sheet("Sheet1")
                .doWrite(dataList);
    }
}
